package com.yanqu.road.logic.xlogic.rank.channel;

import com.yanqu.road.entity.rank.xrank.XChannelRank;
import com.yanqu.road.entity.rank.xrank.XChannelRankPart;
import com.yanqu.road.logic.sort.XChannelRankPartSort;
import com.yanqu.road.logic.xlogic.rank.channel.entiety.XChannelPartListModel;
import com.yanqu.road.logic.xlogic.rank.channel.entiety.XChannelRankPartChangeParam;
import com.yanqu.road.logic.xlogic.rank.channel.entiety.XChannelRankPartChangeResult;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class XChannelRankListModel {

    /**
     * 入库信息
     */
    private String dbName;
    private String tableName;

    private int activityId;
    private int conditionType;
    private int maxRank;
    private Comparator<XChannelRank> rankSort;

    // 榜单不入库，贡献值入库
    private LinkedList<XChannelRank> rankList = new LinkedList<>();

    /**
     * 贡献值
     */
    private Map<Integer, XChannelPartListModel> partMap = new ConcurrentHashMap<>();
    private Map<Integer, Map<Long, XChannelRankPart>> syncPartCache = new ConcurrentHashMap<>();
    private int partMaxRank;

    private int rankCapacity;

    private long allSyncTime;

    public XChannelRankListModel(String dbName, String tableName,
                                 int activityId, int conditionType, int maxRank, Comparator<XChannelRank> rankSort, int partMaxRank) {
        this.dbName = dbName;
        this.tableName = tableName;

        this.activityId = activityId;
        this.conditionType = conditionType;
        this.maxRank = maxRank;
        this.rankSort = rankSort;
        this.partMaxRank = partMaxRank;

        this.rankCapacity = calcRankCapacity(maxRank);
    }

    private int calcRankCapacity(int maxRank) {
        return maxRank + 20;
    }

    public void setMaxRank(int maxRank) {
        this.maxRank = maxRank;
        this.rankCapacity = calcRankCapacity(maxRank);
    }

    public int getMyRank(int channelId) {
        LinkedList<XChannelRank> list = new LinkedList<>(rankList);
        int rank = 0;
        for (XChannelRank channelRank : list) {
            if (++rank > maxRank) {
                return -1;
            }
            if (channelRank.getChannelId() == channelId) {
                return rank;
            }
        }
        return -1;
    }

    public void rankChange(int channelId) {
        synchronized (this) {
            XChannelRank channelRank = new XChannelRank();
            channelRank.setActivityId(activityId);
            channelRank.setConditionType(conditionType);
            channelRank.setChannelId(channelId);
            channelRank.setValue(calChannelValue(channelId));
            channelRank.setLastUpdateTime(System.currentTimeMillis());
            rankChange(channelRank);
        }
    }

    private void rankChange(XChannelRank channelRank) {
        synchronized (this) {
            boolean isChange = false;

            boolean inList = false;
            for (XChannelRank tmpRank : rankList) {
                if (tmpRank.getChannelId() == channelRank.getChannelId()) {
                    boolean valueChange = rankSort.compare(channelRank, tmpRank) != 0;
                    tmpRank.replace(channelRank, valueChange);
                    inList = true;
                    isChange = true;
                }
            }
            if (!inList) {
                if (rankList.size() < rankCapacity) {
                    rankList.add(channelRank);
                    isChange = true;
                } else {
                    XChannelRank lastRank = rankList.getLast();
                    if (rankSort.compare(channelRank, lastRank) < 0) {
                        rankList.removeLast();
                        rankList.add(channelRank);
                        isChange = true;
                    }
                }
            }

            if (isChange) {
                rankList.sort(rankSort);
            }

        }
    }

    public void initByPartMap(List<XChannelRankPart> partList) {
        synchronized (this) {
            // 按channelId拆分
            Map<Integer, LinkedList<XChannelRankPart>> partListMap = new ConcurrentHashMap<>();
            for (XChannelRankPart rankPart : partList) {
                partListMap.putIfAbsent(rankPart.getChannelId(), new LinkedList<>());
                partListMap.get(rankPart.getChannelId()).add(rankPart);
            }

            // 添加到map
            Map<Integer, XChannelPartListModel> tmpPartMap = new ConcurrentHashMap<>();
            for (Map.Entry<Integer, LinkedList<XChannelRankPart>> entry : partListMap.entrySet()) {
                int channelId = entry.getKey();
                LinkedList<XChannelRankPart> value = entry.getValue();

                XChannelPartListModel model = new XChannelPartListModel(dbName, tableName, partMaxRank, new XChannelRankPartSort());
                model.initByRankList(value);
                tmpPartMap.put(channelId, model);
            }
            partMap = tmpPartMap;

            // 榜单更新
            for (Integer channelId : partListMap.keySet()) {
                rankChange(channelId);
            }
        }
    }

    /**
     * 贡献值修改
     */
    public void partValueChange(XChannelRankPart part, boolean needSync, boolean rankChange) {
        synchronized (this) {

            // 修改贡献值
            if (!partMap.containsKey(part.getChannelId())) {
                partMap.put(part.getChannelId(), new XChannelPartListModel(dbName, tableName, partMaxRank, new XChannelRankPartSort()));
            }
            XChannelPartListModel partListModel = partMap.get(part.getChannelId());
            XChannelRankPartChangeResult changeResult = partListModel.rankChange(part, new XChannelRankPartChangeParam(needSync));

            // 添加到待同步
            if (changeResult.isChange() && needSync) {
                if (!syncPartCache.containsKey(part.getChannelId())) {
                    syncPartCache.put(part.getChannelId(), new ConcurrentHashMap<>());
                }
                syncPartCache.get(part.getChannelId()).put(part.getUserId(), part);
            }

            // 榜单变更
            if (changeResult.isChange() && rankChange) {
                rankChange(part.getChannelId());
            }
        }
    }

    /**
     * 获取贡献值变化数据 并清除
     */
    public List<XChannelRankPart> getSyncPartCacheAndClear() {
        Map<Integer, Map<Long, XChannelRankPart>> tmpCache;
        synchronized (this) {
            tmpCache = new ConcurrentHashMap<>(syncPartCache);
            syncPartCache.clear();
        }

        List<XChannelRankPart> resultList = new ArrayList<>();
        for (Map<Long, XChannelRankPart> map : tmpCache.values()) {
            resultList.addAll(map.values());
        }

        return resultList;
    }

    /**
     * 获取地区贡献值排行
     */
    public List<XChannelRankPart> getPartValueRankList(int channelId) {
        if (!partMap.containsKey(channelId)) {
            return new ArrayList<>();
        }
        return partMap.get(channelId).getRankList();
    }

    /**
     * 计算区服总值
     */
    private BigInteger calChannelValue(int channelId) {
        BigInteger totalValue = BigInteger.ZERO;
        if (!partMap.containsKey(channelId)) {
            return totalValue;
        }
        List<XChannelRankPart> tmpList = partMap.get(channelId).getRankList();
        for (XChannelRankPart part : tmpList) {
            totalValue = totalValue.add(part.getValue());
        }
        return totalValue;
    }


    public void setRankList(LinkedList<XChannelRank> rankList) {
        synchronized (this) {
            this.rankList = rankList;
            this.rankList.sort(rankSort);
            // 去掉多余的
            while (this.rankList.size() > rankCapacity) {
                this.rankList.removeLast();
            }
        }
    }

    public List<XChannelRank> getRankList() {
        return getRankList(maxRank);
    }

    public List<XChannelRank> getRankList(int maxCount) {
        List<XChannelRank> list = new LinkedList<>();
        for (XChannelRank rank : new LinkedList<>(rankList)) {
            list.add(rank);
            if (list.size() >= maxCount) {
                break;
            }
        }
        return list;
    }

    public int getActivityId() {
        return activityId;
    }

    public int getConditionType() {
        return conditionType;
    }

    public int getMaxRank() {
        return maxRank;
    }

    public Comparator<XChannelRank> getRankSort() {
        return rankSort;
    }

    public void save() {
        for (XChannelPartListModel model : partMap.values()) {
            model.save();
        }
    }

    public long getAllSyncTime() {
        return allSyncTime;
    }

    public void setAllSyncTime(long allSyncTime) {
        this.allSyncTime = allSyncTime;
    }

    public Map<Integer, XChannelPartListModel> getPartMap() {
        return partMap;
    }
}
